package com.atguigu.tingshu.search.service.impl;

import co.elastic.clients.json.JsonData;
import com.alibaba.fastjson.JSON;
import com.atguigu.tingshu.common.constant.RedisConstant;
import com.atguigu.tingshu.common.constant.SystemConstant;
import com.atguigu.tingshu.common.util.PinYinUtils;
import com.atguigu.tingshu.model.search.SuggestIndex;
import com.atguigu.tingshu.search.repository.SuggestIndexRepository;
import cn.hutool.core.util.PageUtil;
import co.elastic.clients.elasticsearch.ElasticsearchClient;
import co.elastic.clients.elasticsearch._types.FieldSort;
import co.elastic.clients.elasticsearch._types.FieldValue;
import co.elastic.clients.elasticsearch._types.SortOptions;
import co.elastic.clients.elasticsearch._types.SortOrder;
import co.elastic.clients.elasticsearch._types.aggregations.*;
import co.elastic.clients.elasticsearch._types.query_dsl.*;
import co.elastic.clients.elasticsearch.core.SearchRequest;
import co.elastic.clients.elasticsearch.core.SearchResponse;
import co.elastic.clients.elasticsearch.core.search.*;
import com.atguigu.tingshu.album.client.AlbumInfoFeignClient;
import com.atguigu.tingshu.album.client.CategoryFeignClient;
import com.atguigu.tingshu.common.result.Result;
import com.atguigu.tingshu.model.album.AlbumAttributeValue;
import com.atguigu.tingshu.model.album.AlbumInfo;
import com.atguigu.tingshu.model.album.BaseCategory3;
import com.atguigu.tingshu.model.album.BaseCategoryView;
import com.atguigu.tingshu.model.search.AlbumInfoIndex;
import com.atguigu.tingshu.model.search.AttributeValueIndex;
import com.atguigu.tingshu.model.user.UserInfo;
import com.atguigu.tingshu.query.search.AlbumIndexQuery;
import com.atguigu.tingshu.search.repository.AlbumIndexRepository;
import com.atguigu.tingshu.search.service.SearchService;
import com.atguigu.tingshu.user.client.UserInfoFeignClient;
import com.atguigu.tingshu.vo.album.AlbumStatVo;
import com.atguigu.tingshu.vo.album.TrackStatMqVo;
import com.atguigu.tingshu.vo.search.AlbumInfoIndexSearchVo;
import com.atguigu.tingshu.vo.search.AlbumInfoIndexVo;
import com.atguigu.tingshu.vo.search.AlbumSearchResponseVo;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RBloomFilter;
import org.redisson.api.RedissonClient;
import org.rocksdb.BloomFilter;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.elasticsearch.core.suggest.Completion;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;


@Slf4j
@Service
@SuppressWarnings({"unchecked", "rawtypes"})
public class SearchServiceImpl implements SearchService {


    @Autowired
    private AlbumIndexRepository albumIndexRepository;

    @Autowired
    private AlbumInfoFeignClient albumInfoFeignClient;

    @Autowired
    private CategoryFeignClient categoryFeignClient;

    @Autowired
    private UserInfoFeignClient userInfoFeignClient;

    @Autowired
    private ElasticsearchClient elasticsearchClient;

    @Autowired
    private SuggestIndexRepository suggestIndexRepository;


    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private RedissonClient redissonClient;


    //上架专辑
    @Override
    public void upperAlbum(Long albumId) {

        AlbumInfoIndex albumInfoIndex = new AlbumInfoIndex();

        CompletableFuture<AlbumInfo> albumInfoCompletableFuture = CompletableFuture.supplyAsync(() -> {
            //远程调用,专辑列表相关数据
            Result<AlbumInfo> albumInfoResult = albumInfoFeignClient.getAlbumInfo(albumId);
            AlbumInfo albumInfo = albumInfoResult.getData();
            albumInfoIndex.setId(albumId);
            albumInfoIndex.setAlbumTitle(albumInfo.getAlbumTitle());
            albumInfoIndex.setAlbumIntro(albumInfo.getAlbumIntro());
            albumInfoIndex.setCoverUrl(albumInfo.getCoverUrl());
            albumInfoIndex.setIncludeTrackCount(albumInfo.getIncludeTrackCount());
            albumInfoIndex.setIsFinished(String.valueOf(albumInfo.getIsFinished()));
            albumInfoIndex.setPayType(albumInfo.getPayType());
            albumInfoIndex.setCreateTime(albumInfo.getCreateTime());


            //从albumInfo中获取albumAttributeValueVoList,赋值给AlbumInfoIndex中的AttributeValueIndex
            List<AlbumAttributeValue> albumAttributeValueVoList = albumInfo.getAlbumAttributeValueVoList();
            List<AttributeValueIndex> attributeValueIndexList = albumAttributeValueVoList.stream().map(albumAttributeValue -> {
                AttributeValueIndex attributeValueIndex = new AttributeValueIndex();
                attributeValueIndex.setAttributeId(albumAttributeValue.getAttributeId());
                attributeValueIndex.setValueId(albumAttributeValue.getValueId());
                return attributeValueIndex;
            }).collect(Collectors.toList());
            albumInfoIndex.setAttributeValueIndexList(attributeValueIndexList);


            return albumInfo;
        });


        CompletableFuture<Void> completableFuture = albumInfoCompletableFuture.thenAcceptAsync((albumInfo -> {
            //远程调用,用户列表相关数据
            Long userId = albumInfo.getUserId();
            Result<UserInfo> userInfoById = userInfoFeignClient.getUserInfoById(userId);
            UserInfo userInfo = userInfoById.getData();
            albumInfoIndex.setAnnouncerName(userInfo.getNickname());

        }));


        CompletableFuture<Void> completableFuture1 = albumInfoCompletableFuture.thenAcceptAsync((albumInfo -> {

            //远程调用,分类列表相关数据
            Long category3Id = albumInfo.getCategory3Id();
            Result<BaseCategoryView> baseCategoryViewResult = categoryFeignClient.categoryfindAttribute(category3Id);
            BaseCategoryView categoryView = baseCategoryViewResult.getData();
            albumInfoIndex.setCategory1Id(categoryView.getCategory1Id());
            albumInfoIndex.setCategory2Id(categoryView.getCategory2Id());
            albumInfoIndex.setCategory3Id(category3Id);
        }));


        CompletableFuture<Void> completableFuture2 = CompletableFuture.runAsync(() -> {
            //远程调用,统计列表相关数据
            Result<AlbumStatVo> albumStatResult = albumInfoFeignClient.findAlbumStatByAlbumId(albumId);
            AlbumStatVo albumStatVo = albumStatResult.getData();
            albumInfoIndex.setPlayStatNum(albumStatVo.getPlayStatNum());
            albumInfoIndex.setSubscribeStatNum(albumStatVo.getSubscribeStatNum());
            albumInfoIndex.setBuyStatNum(albumStatVo.getBuyStatNum());
            albumInfoIndex.setCommentStatNum(albumStatVo.getCommentStatNum());

            //热度评分
            double hotScore = albumInfoIndex.getSubscribeStatNum() * 0.2 +
                    albumInfoIndex.getBuyStatNum() * 0.3 +
                    albumInfoIndex.getCommentStatNum() * 0.4 +
                    albumInfoIndex.getPlayStatNum() * 0.1;
            albumInfoIndex.setHotScore(hotScore);
        });

        CompletableFuture.allOf(albumInfoCompletableFuture, completableFuture, completableFuture1, completableFuture2).join();

        log.info("albumInfoIndex:{}", albumInfoIndex);
        // 把专辑数据保存到ES索引库中
        albumIndexRepository.save(albumInfoIndex);
        // 初始化提词库
        CompletableFuture.runAsync(() -> {
            initSuggestIndex(albumInfoIndex);
        });

        //获取布隆过滤器
        RBloomFilter<Object> bloomFilter = redissonClient.getBloomFilter(RedisConstant.ALBUM_BLOOM_FILTER);
        //当专辑上架的时候向布隆过滤器中添加专辑的id
        bloomFilter.add(albumId);
    }

    private void initSuggestIndex(AlbumInfoIndex albumInfoIndex) {
        // 保存专辑标题数据
        SuggestIndex albumTitleSuggestIndex = new SuggestIndex();
        String id = UUID.randomUUID().toString().replace("-", "");
        albumTitleSuggestIndex.setId(id);
        albumTitleSuggestIndex.setTitle(albumInfoIndex.getAlbumTitle());
        albumTitleSuggestIndex.setKeyword(new Completion(new String[]{albumInfoIndex.getAlbumTitle()}));
        albumTitleSuggestIndex.setKeywordPinyin(new Completion(new String[]{PinYinUtils.toHanyuPinyin(albumInfoIndex.getAlbumTitle())}));   // PinYinUtils底层是对Pinying4J的api进行封装
        albumTitleSuggestIndex.setKeywordSequence(new Completion(new String[]{PinYinUtils.getFirstLetter(albumInfoIndex.getAlbumTitle())}));
        suggestIndexRepository.save(albumTitleSuggestIndex);

        // 保存专辑的简介数据
        SuggestIndex albumIntroSuggestIndex = new SuggestIndex();
        albumIntroSuggestIndex.setId(UUID.randomUUID().toString().replace("-", ""));
        albumIntroSuggestIndex.setTitle(albumInfoIndex.getAlbumIntro());
        albumIntroSuggestIndex.setKeyword(new Completion(new String[]{albumInfoIndex.getAlbumIntro()}));
        albumIntroSuggestIndex.setKeywordPinyin(new Completion(new String[]{PinYinUtils.toHanyuPinyin(albumInfoIndex.getAlbumIntro())}));   // PinYinUtils底层是对Pinying4J的api进行封装
        albumIntroSuggestIndex.setKeywordSequence(new Completion(new String[]{PinYinUtils.getFirstLetter(albumInfoIndex.getAlbumIntro())}));
        suggestIndexRepository.save(albumIntroSuggestIndex);

        // 保存专辑的作者数据
        SuggestIndex announcerNameSuggestIndex = new SuggestIndex();
        announcerNameSuggestIndex.setId(UUID.randomUUID().toString().replace("-", ""));
        announcerNameSuggestIndex.setTitle(albumInfoIndex.getAnnouncerName());
        announcerNameSuggestIndex.setKeyword(new Completion(new String[]{albumInfoIndex.getAnnouncerName()}));
        announcerNameSuggestIndex.setKeywordPinyin(new Completion(new String[]{PinYinUtils.toHanyuPinyin(albumInfoIndex.getAnnouncerName())}));   // PinYinUtils底层是对Pinying4J的api进行封装
        announcerNameSuggestIndex.setKeywordSequence(new Completion(new String[]{PinYinUtils.getFirstLetter(albumInfoIndex.getAnnouncerName())}));
        suggestIndexRepository.save(announcerNameSuggestIndex);
    }

    //下架专辑
    @Override
    public void getLower(Long albumId) {
        albumIndexRepository.deleteById(albumId);

        //创建一个新的布隆过滤器
        RBloomFilter<Long> bloomFilter = redissonClient.getBloomFilter(RedisConstant.ALBUM_BLOOM_FILTER_NEW);
        bloomFilter.tryInit(1000000, 0.000001);
        log.info("创建一个新的布隆过滤器");
        //获取未被删除的专辑id
        Iterable<AlbumInfoIndex> albumIndexRepositoryAll = albumIndexRepository.findAll();
        albumIndexRepositoryAll.forEach(albumInfoIndex -> {
            Long esAlbumId = albumInfoIndex.getId();
            bloomFilter.add(esAlbumId);
        });

        //为了保证删除旧的布隆过滤器和重命名新的布隆过滤器之间的原子性,那么就需要使用redis中的lua脚本
        String script = "redis.call('del' , KEYS[1])\n" +
                "redis.call('del' , \"{\" .. KEYS[1] .. \"}:config\")\n" +
                "redis.call('rename' , KEYS[2] , KEYS[1])\n" +
                "redis.call('rename' , \"{\" .. KEYS[2] .. \"}:config\" , \"{\" .. KEYS[1] .. \"}:config\")\n" +
                "return 1 ";

        Long result = (Long) redisTemplate.execute(new DefaultRedisScript(script, Long.class), Arrays.asList(RedisConstant.ALBUM_BLOOM_FILTER, RedisConstant.ALBUM_BLOOM_FILTER_NEW));
        if (result == 1) {
            log.info("新的布隆过滤器创建成功");
        }
    }


    @SneakyThrows
    @Override
    public AlbumSearchResponseVo searchAlbumInfo(AlbumIndexQuery albumIndexQuery) {


        // 构建搜索请求对象
        SearchRequest searchRequest = this.buildQueryDsl(albumIndexQuery);
        // 发送请求,得到结果对象
        SearchResponse<AlbumInfoIndex> searchResponse = elasticsearchClient.search(searchRequest, AlbumInfoIndex.class);

        // 解析结果搜索结果对象，得到响应结果数据
        AlbumSearchResponseVo albumSearchResponseVo = parseSearchResult(searchResponse, albumIndexQuery);


        return albumSearchResponseVo;
    }


    //获取频道页数据
    @Override
    @SneakyThrows
    public List<AlbumInfoIndexSearchVo> channel(Long category1Id) {

        // 根据一级分类的id查找置顶的三级分类数据
        Result<List<BaseCategory3>> result = categoryFeignClient.findTopBaseCategory3(category1Id);
        List<BaseCategory3> baseCategory3List = result.getData();

        // 创建TermsQueryField对象
        List<FieldValue> fieldValueList = baseCategory3List.stream().map(baseCategory3 -> {
            Long id = baseCategory3.getId();
            FieldValue fieldValue = new FieldValue.Builder().longValue(id).build();
            return fieldValue;
        }).collect(Collectors.toList());
        TermsQueryField termsQueryField = new TermsQueryField.Builder()
                .value(fieldValueList)
                .build();

        // 创建一个BoolQuery对象
        BoolQuery boolQuery = QueryBuilders.bool()
                .must(builder -> builder.term(QueryBuilders.term().field("category1Id").value(category1Id).build()))
                .must(builder -> builder.terms(QueryBuilders.terms().field("category3Id").terms(termsQueryField).build()))
                .build();
        Query query = new Query.Builder()
                .bool(boolQuery)
                .build();

        // 创建Aggregation对象
        SortOptions sortOptions = new SortOptions.Builder().field(builder -> builder.field("hotScore").order(SortOrder.Desc)).build();
        Aggregation albumInfoAggregation = new Aggregation.Builder().topHits(builder -> builder.size(6).sort(sortOptions)).build();

        // 创建一个Aggregation对象
        Aggregation aggregation = new Aggregation.Builder()
                .terms(builder -> builder.field("category3Id")
                        .size(10))
                .aggregations("albumInfoAgg", albumInfoAggregation)
                .build();

        // 创建SearchRequest对象
        SourceConfig sourceConfig = new SourceConfig.Builder().fetch(false).build();
        SearchRequest searchRequest = new SearchRequest.Builder()
                .index("albuminfo")
                .query(query)
                .source(sourceConfig)
                .aggregations("category3IdAgg", aggregation)
                .build();

        // 执行搜索
        SearchResponse<AlbumInfoIndex> indexSearchResponse = elasticsearchClient.search(searchRequest, AlbumInfoIndex.class);
        Map<String, Aggregate> aggregations = indexSearchResponse.aggregations();
        Aggregate aggregate = aggregations.get("category3IdAgg");
        LongTermsAggregate longTermsAggregate = (LongTermsAggregate) aggregate._get();
        Buckets<LongTermsBucket> bucketBuckets = longTermsAggregate.buckets();
        List<LongTermsBucket> longTermsBuckets = bucketBuckets.array();

        List<AlbumInfoIndexSearchVo> albumInfoIndexSearchVos = new ArrayList<>();
        for (LongTermsBucket longTermsBucket : longTermsBuckets) {

            AlbumInfoIndexSearchVo albumInfoIndexSearchVo = new AlbumInfoIndexSearchVo();

            // 获取三级分类的id
            long c3Id = longTermsBucket.key();
            BaseCategory3 dbBaseCategory3 = baseCategory3List.stream().filter(baseCategory3 -> baseCategory3.getId() == c3Id)
                    .collect(Collectors.toList()).get(0);

            Map<String, Aggregate> aggregateMap = longTermsBucket.aggregations();
            Aggregate albumInfoAgg = aggregateMap.get("albumInfoAgg");
            TopHitsAggregate topHitsAggregate = (TopHitsAggregate) albumInfoAgg._get();
            HitsMetadata<JsonData> hitsMetadata = topHitsAggregate.hits();
            List<Hit<JsonData>> hits = hitsMetadata.hits();

            // 获取文档数据
            List<AlbumInfoIndex> albumInfoIndexList = hits.stream().map(hit -> {
                JsonData jsonData = hit.source();
                AlbumInfoIndex albumInfoIndex = jsonData.to(AlbumInfoIndex.class);
                return albumInfoIndex;
            }).collect(Collectors.toList());

            albumInfoIndexSearchVo.setBaseCategory3(dbBaseCategory3);
            albumInfoIndexSearchVo.setList(albumInfoIndexList);

            albumInfoIndexSearchVos.add(albumInfoIndexSearchVo);

        }

        return albumInfoIndexSearchVos;
    }


    //关键字自动补全功能
    @Override
    @SneakyThrows
    public List<String> completeSuggest(String keyword) {

        CompletionSuggester completionSuggester1 = new CompletionSuggester.Builder()
                .skipDuplicates(true)
                .size(10)
                .fuzzy(fuzzyBuilder -> fuzzyBuilder.fuzziness("auto"))
                .field("keyword").build();
        FieldSuggester fieldSuggester1 = new FieldSuggester.Builder().completion(completionSuggester1).prefix(keyword).build();

        CompletionSuggester completionSuggester2 = new CompletionSuggester.Builder()
                .skipDuplicates(true)
                .size(10)
                .fuzzy(fuzzyBuilder -> fuzzyBuilder.fuzziness("auto"))
                .field("keyword").build();
        FieldSuggester fieldSuggester2 = new FieldSuggester.Builder().completion(completionSuggester2).prefix(keyword).build();

        CompletionSuggester completionSuggester3 = new CompletionSuggester.Builder()
                .skipDuplicates(true)
                .size(10)
                .fuzzy(fuzzyBuilder -> fuzzyBuilder.fuzziness("auto"))
                .field("keyword").build();
        FieldSuggester fieldSuggester3 = new FieldSuggester.Builder().completion(completionSuggester3).prefix(keyword).build();

        Suggester suggester = new Suggester.Builder()
                .suggesters("suggestionKeyword", fieldSuggester1)
                .suggesters("suggestionKeywordPinyin", fieldSuggester2)
                .suggesters("suggestionKeywordSequence", fieldSuggester3)
                .build();

        SearchRequest searchRequest = new SearchRequest.Builder()
                .index("suggestinfo")
                .suggest(suggester)
                .build();
        SearchResponse<SuggestIndex> searchResponse = elasticsearchClient.search(searchRequest, SuggestIndex.class);

        Map<String, List<Suggestion<SuggestIndex>>> suggest = searchResponse.suggest();

        List<String> results = new ArrayList<String>();
        getSuggestResult(results, suggest, "suggestionKeyword");
        getSuggestResult(results, suggest, "suggestionKeywordPinyin");
        getSuggestResult(results, suggest, "suggestionKeywordSequence");
        return results;
    }

    //更新排行榜
    @Override
    @SneakyThrows
    public void updateLatelyAlbumRanking() {


        Aggregation.Builder.ContainerBuilder containerBuilder = new Aggregation.Builder().terms(builder -> builder.field("category1Id").size(40));

        String[] rankingDimensionArray = new String[]{"hotScore", "playStatNum", "subscribeStatNum", "buyStatNum", "commentStatNum"};
        for (String rankingDimension : rankingDimensionArray) {

            SortOptions sortOptions = new SortOptions.Builder()
                    .field(builder -> builder.field(rankingDimension).order(SortOrder.Desc))
                    .build();

            Aggregation aggregation = new Aggregation.Builder()
                    .topHits(builder -> builder.size(10).sort(sortOptions))
                    .build();
            containerBuilder.aggregations(rankingDimension + "TopHitsAgg", aggregation);
        }

        SearchRequest searchRequest = new SearchRequest.Builder()
                .index("albuminfo")
                .query(builder -> builder.matchAll(new MatchAllQuery.Builder().build()))
                .aggregations("category1Aggs", containerBuilder.build())
                .source(builder -> builder.fetch(false))
                .build();

        SearchResponse<AlbumInfo> searchResponse = elasticsearchClient.search(searchRequest, AlbumInfo.class);

        // 将统计查询的结果数据存储到Redis
        Map<String, Aggregate> stringAggregateMap = searchResponse.aggregations();
        Aggregate category1Aggs = stringAggregateMap.get("category1Aggs");
        LongTermsAggregate longTermsAggregate = (LongTermsAggregate) category1Aggs._get();
        Buckets<LongTermsBucket> buckets = longTermsAggregate.buckets();
        List<LongTermsBucket> longTermsBucketList = buckets.array();
        for (LongTermsBucket longTermsBucket : longTermsBucketList) {
            long key = longTermsBucket.key(); // 获取一级分类的id
            String rangingRedisKey = RedisConstant.RANKING_KEY_PREFIX + key;
            Map<String, Aggregate> aggregations = longTermsBucket.aggregations();
            for (String rankingDimension : rankingDimensionArray) {
                Aggregate aggregate = aggregations.get(rankingDimension + "TopHitsAgg");
                TopHitsAggregate topHitsAggregate = (TopHitsAggregate) aggregate._get();
                HitsMetadata<JsonData> hitsMetadata = topHitsAggregate.hits();
                List<Hit<JsonData>> hitList = hitsMetadata.hits();
                List<AlbumInfoIndex> albumInfoIndexList = new ArrayList<>();
                for (Hit<JsonData> hit : hitList) {
                    AlbumInfoIndex albumInfoIndex = hit.source().to(AlbumInfoIndex.class);
                    albumInfoIndexList.add(albumInfoIndex);
                }
                redisTemplate.opsForHash().put(rangingRedisKey, rankingDimension, JSON.toJSONString(albumInfoIndexList));
            }

        }
    }


    //获取排行榜列表
    @Override
    public List<AlbumInfoIndex> findRankingList(Long category1Id, String dimension) {
        String rankingRedisKey = RedisConstant.RANKING_KEY_PREFIX + category1Id;
        Object obj = redisTemplate.opsForHash().get(rankingRedisKey, dimension);
        if (obj != null) {
            List<AlbumInfoIndex> albumInfoIndexList = JSON.parseArray(obj.toString(), AlbumInfoIndex.class);
            return albumInfoIndexList;
        } else {
            return new ArrayList<>();
        }

    }

    @Override
    public void albumStatUpdate(TrackStatMqVo trackStatMqVo) {
        Long albumId = trackStatMqVo.getAlbumId();
        Optional<AlbumInfoIndex> repositoryById = albumIndexRepository.findById(albumId);
        AlbumInfoIndex albumInfoIndex = repositoryById.get();

        String statType = trackStatMqVo.getStatType();
        switch (statType) {
            case SystemConstant.ALBUM_STAT_PLAY:
                albumInfoIndex.setPlayStatNum(albumInfoIndex.getPlayStatNum() + trackStatMqVo.getCount());
                break;
            case SystemConstant.ALBUM_STAT_SUBSCRIBE:
                albumInfoIndex.setSubscribeStatNum(albumInfoIndex.getSubscribeStatNum() + trackStatMqVo.getCount());
                break;
            case SystemConstant.ALBUM_STAT_BROWSE:
                albumInfoIndex.setBuyStatNum(albumInfoIndex.getBuyStatNum() + trackStatMqVo.getCount());
                break;
            case SystemConstant.ALBUM_STAT_COMMENT:
                albumInfoIndex.setCommentStatNum(albumInfoIndex.getCommentStatNum() + trackStatMqVo.getCount());
        }
        albumIndexRepository.save(albumInfoIndex);
        log.info("更新专辑统计数据成功");
    }

    private void getSuggestResult(List<String> results, Map<String, List<Suggestion<SuggestIndex>>> suggest, String suggestionKeywordSequence) {

        List<Suggestion<SuggestIndex>> suggestions = suggest.get(suggestionKeywordSequence);
        for (Suggestion<SuggestIndex> suggestion : suggestions) {
            CompletionSuggest<SuggestIndex> completion = suggestion.completion();
            List<CompletionSuggestOption<SuggestIndex>> options = completion.options();
            options.stream().forEach(option -> {
                SuggestIndex source = option.source();
                results.add(source.getTitle());
            });
        }
    }


    // 解析结果搜索结果对象，得到响应结果数据
    private AlbumSearchResponseVo parseSearchResult(SearchResponse<AlbumInfoIndex> searchResponse, AlbumIndexQuery albumIndexQuery) {

        HitsMetadata<AlbumInfoIndex> hitsMetadata = searchResponse.hits();

        List<Hit<AlbumInfoIndex>> hits = hitsMetadata.hits();
        List<AlbumInfoIndexVo> albumInfoIndex1 = hits.stream().map(hit -> {
            AlbumInfoIndex albumInfoIndex = hit.source();
            AlbumInfoIndexVo albumInfoIndexVo = new AlbumInfoIndexVo();
            BeanUtils.copyProperties(albumInfoIndex, albumInfoIndexVo);
            Map<String, List<String>> stringListMap = hit.highlight();
            if (!StringUtils.isEmpty(stringListMap)) {
                List<String> albumTitle = stringListMap.get("albumTitle");
                if (!CollectionUtils.isEmpty(albumTitle)) {
                    String albumTitleHighLight = albumTitle.get(0);
                    albumInfoIndexVo.setAlbumTitle(albumTitleHighLight);
                }

                List<String> albumIntro = stringListMap.get("albumIntro");
                if (!CollectionUtils.isEmpty(albumIntro)) {
                    String albumIntroHighLight = albumIntro.get(0);
                    albumInfoIndexVo.setAlbumIntro(albumIntroHighLight);
                }
            }
            return albumInfoIndexVo;
        }).collect(Collectors.toList());


        AlbumSearchResponseVo albumSearchResponseVo = new AlbumSearchResponseVo();
        albumSearchResponseVo.setList(albumInfoIndex1);

        //解析分页
        TotalHits total = hitsMetadata.total();
        albumSearchResponseVo.setTotal(total.value());
        albumSearchResponseVo.setPageNo(albumIndexQuery.getPageNo());
        albumSearchResponseVo.setPageSize(albumIndexQuery.getPageSize());
        Integer totalPage = PageUtil.totalPage(total.value(), albumIndexQuery.getPageSize());
        albumSearchResponseVo.setTotalPages(Long.parseLong(totalPage.toString()));
        return albumSearchResponseVo;
    }

    // 构建搜索请求对象
    private SearchRequest buildQueryDsl(AlbumIndexQuery albumIndexQuery) {
        //  创建BoolQuery对象
        BoolQuery.Builder builder = new BoolQuery.Builder();
        // 拼接KeyWord的搜索参数
        String keyword = albumIndexQuery.getKeyword();
        if (!StringUtils.isEmpty(keyword)) {
            Query query = QueryBuilders.multiMatch(builder1 -> builder1.fields("albumTitle", "albumIntro").query(keyword));
            builder.must(query);
        } else {
            Query query = QueryBuilders.matchAll().build()._toQuery();
            builder.must(query);
        }

        // 设置分类搜索条件
        if (albumIndexQuery.getCategory1Id() != null) {
            Query query = QueryBuilders.term(builder1 -> builder1.field("category1Id").value(albumIndexQuery.getCategory1Id()));
            builder.must(query);
        }
        if (albumIndexQuery.getCategory2Id() != null) {
            Query query = QueryBuilders.term(builder1 -> builder1.field("category2Id").value(albumIndexQuery.getCategory2Id()));
            builder.must(query);
        }
        if (albumIndexQuery.getCategory3Id() != null) {
            Query query = QueryBuilders.term(builder1 -> builder1.field("category3Id").value(albumIndexQuery.getCategory3Id()));
            builder.must(query);
        }
        // 拼接专辑搜索的搜索条件
        List<String> attributeList = albumIndexQuery.getAttributeList();
        if (!CollectionUtils.isEmpty(attributeList)) {
            for (String attribute : attributeList) {
                String[] split = attribute.split(":");
                String attributeId = split[0];
                String attributeValueId = split[1];

                TermQuery termQuery2 = new TermQuery.Builder().field(attributeValueId).build();
                Query query2 = new Query.Builder().term(termQuery2).build();
                TermQuery termQuery1 = new TermQuery.Builder().field(attributeId).build();
                Query query1 = new Query.Builder().term(termQuery1).build();
                BoolQuery boolQuery = new BoolQuery.Builder().must(query1).must(query2).build();
                Query query = new Query.Builder().bool(boolQuery).build();
                new Query.Builder().nested(builder1 -> builder1.path("attributeValueIndexList").query(query)).build();
                builder.filter(query);
            }
        }


        //拼接排序
        String order = albumIndexQuery.getOrder();
        FieldSort.Builder fieldSortBuilder = new FieldSort.Builder();
        if (!StringUtils.isEmpty(order)) {
            String[] split = order.split(":");
            String sortField = split[0];
            String orde = split[1];
            switch (sortField) {
                case "1":
                    fieldSortBuilder.field("hotScore").order("asc".equalsIgnoreCase(orde) ? SortOrder.Asc : SortOrder.Desc);
                    break;
                case "2":
                    fieldSortBuilder.field("playStatNum").order("asc".equalsIgnoreCase(orde) ? SortOrder.Asc : SortOrder.Desc);
                    break;
                case "3":
                    fieldSortBuilder.field("createTime").order("asc".equalsIgnoreCase(orde) ? SortOrder.Asc : SortOrder.Desc);
                    break;
            }
        } else {
            fieldSortBuilder.field("hotScore").order(SortOrder.Desc);
        }


        //设置分页
        SortOptions sortOptions = new SortOptions.Builder()
                .field(fieldSortBuilder.build()).build();


        //设置高亮
        HighlightField highlightField = new HighlightField.Builder()
                .preTags("<font color='red'>")
                .postTags("</font>")
                .build();
        Highlight highlight = new Highlight.Builder().fields("albumTitle", highlightField)
                .fields("albumIntro", highlightField).build();

        Query query = new Query.Builder().bool(builder.build()).build();
        SearchRequest searchRequest = new SearchRequest.Builder()
                .sort(sortOptions)
                .from((albumIndexQuery.getPageNo() - 1) * albumIndexQuery.getPageSize())
                .size(albumIndexQuery.getPageSize())
                .highlight(highlight)
                .index("albuminfo").query(query).build();
        return searchRequest;
    }


}
